//
// Created by user on 20-4-14.
//
#include "APIC.h"

IOAPICMap ioapicMap;

void APICDeviceInit() {

    unsigned int x;
    unsigned int *p = NULL;

    // 1. 映射 I/O APIC 页表
    IOAPICPageTableRemap();

    // 2. 往 IDT 表设置中断函数
    for (unsigned int i = 32; i < 56; i++) {

        set_intr_gate(i, 2, interrupt[i - 32]);

    }

    // 3. 屏蔽 8259A 中断控制器
    printfColor(GREEN, BLACK, "MASK 8259A\n");
    io_out8(0x21, 0xff);
    io_out8(0xa1, 0xff);

    // 4. 开启 IMCR
    io_out8(0x22, 0x70);
    io_out8(0x23, 0x01);

    // 5. 初始化 Local APIC
    LocalAPICInit();

    // 6. 初始化 I/O APIC
    IOAPICInit();


//    io_out32(0xcf8, 0x8000f8f0);

    x = io_in32(0xcfc);
    printfColor(RED, BLACK, "Get RCBA Address: %lx\n", x);
    x = x & 0xffffc000;
    printfColor(RED, BLACK, "Get RCBA Address: %lx\n", x);

    if (x > 0xfec00000 && x < 0xfee00000) {

        p = (unsigned int *) Physical_To_Virtual(x + 0x31feUL);

        x = (*p & 0xffffff00) | 0x100;

        printfColor(GREEN, BLACK, "234!\n");
        mfence();
        *p = x;
        mfence();

    }

    // 5. 清空中断处理描述符表数组
    memset(interruptDescriptor, 0, sizeof(IRPDescriptorTable) * NR_IRQS);

    // 6. 开中断
//    sti();

//    printfColor(GREEN, BLACK, "test!\n");
}

void LocalAPICInit() {

    unsigned int x, y;
    unsigned int eax, ebx, ecx, edx;

    // 1. 通过 CPUID.01功能查询Local APIC是否支持APIC、xAPIC和x2APIC
    getCpuId(1, 0, &eax, &ebx, &ecx, &edx);

    // 2. 判断 Local APIC 是否支持APIC和xAPIC
    if ((1 << 9) & edx)
        printfColor(WHITE, BLACK, "HW support APIC&xAPIC\n");
    else
        printfColor(WHITE, BLACK, "HW NO support APIC&xAPIC\n");

    // 3. 判断 Local APIC 是否支持 x2APIC
    if ((1 << 21) & ecx)
        printfColor(WHITE, BLACK, "HW support x2APIC\n");
    else
        printfColor(WHITE, BLACK, "HW NO support x2APIC\n");

    // 4. 通过 rdmsr 指令把当前 IA32_APIC_BASE 寄存器的值读出来
    // 然后通过 wrmsr 指令置位 IA32_APIC_BASE 寄存器的第10位和第11位来开启 Local APIC 的工作模式
    // 最后再把 IA32_APIC_BASE 的值读出来
    __asm__ __volatile__(   "movq 	$0x1b,	%%rcx	\n\t"
            "rdmsr	\n\t"
            "bts	$10,	%%rax	\n\t"
            "bts	$11,	%%rax	\n\t"
            "wrmsr	\n\t"
            "movq 	$0x1b,	%%rcx	\n\t"
            "rdmsr	\n\t"
    :"=a"(x), "=d"(y)
    :
    :"memory");

    printfColor(WHITE, BLACK, "eax: %lx,edx: %lx\n", x, y);

    // 5. 判断 xAPIC 和 x2APIC 是否开启成功
    if (x & 0xc00)
        printfColor(WHITE, BLACK, "xAPIC & x2APIC enabled\n");

    // 6. 首先通过 rdmsr 把 SVR 寄存器的值读出来
    // 然后通过 wrmsr 置位 SVR 寄存器的第8位和第12位来开启Local APIC和禁止广播EOI消息
    // 最后再把改好的 SVR 寄存器的值读出来
    __asm__ __volatile__(    "movq 	$0x80f,	%%rcx	\n\t"
            "rdmsr	\n\t"
            "bts	$8,	%%rax	\n\t"
//            "bts	$12,%%rax\n\t"
            "wrmsr	\n\t"
            "movq 	$0x80f,	%%rcx	\n\t"
            "rdmsr	\n\t"
    :"=a"(x), "=d"(y)
    :
    :"memory");

    printfColor(WHITE, BLACK, "eax: %lx,edx: %lx\n", x, y);

    // 7. 判断开启Local APIC是否成功
    if (x & 0x100)
        printfColor(WHITE, BLACK, "SVR[8] enabled\n");

    // 8. 判断禁止广播EOI消息是否成功
    if (x & 0x1000)
        printfColor(WHITE, BLACK, "SVR[12] enabled\n");
    else
        printfColor(INDIGO, BLACK, "SVR[12] faild!\n");

    // 9. 获取Local APIC的ID值
    __asm__ __volatile__(    "movq $0x802,	%%rcx	\n\t"
            "rdmsr	\n\t"
    :"=a"(x), "=d"(y)
    :
    :"memory");

    printfColor(INDIGO, BLACK, "eax: %x, edx: %x x2APIC ID: %x\n", x, y, x);

    // 10. 获取Local APIC的版本号
    __asm__ __volatile__(    "movq $0x803,	%%rcx	\n\t"
            "rdmsr	\n\t"
    :"=a"(x), "=d"(y)
    :
    :"memory");

    printfColor(INDIGO, BLACK, "local APIC Version: %lx,Max LVT Entry: %lx, SVR(Suppress EOI Broadcast): %lx\t",
                x & 0xff, (x >> 16 & 0xff) + 1, x >> 24 & 0x1);

    if ((x & 0xff) < 0x10)
        printfColor(INDIGO, BLACK, "82489DX discrete APIC\n");
    else if (((x & 0xff) >= 0x10) && ((x & 0xff) <= 0x15))
        printfColor(INDIGO, BLACK, "Integrated APIC\n");

    // 11. 屏蔽LVT中的所有中断投递功能，因为还没有配置中断程序
    __asm__ __volatile__(    "movq 	$0x82f,	%%rcx	\n\t"    //CMCI
//            "wrmsr	\n\t"
            "movq 	$0x832,	%%rcx	\n\t"    //Timer
            "wrmsr	\n\t"
            "movq 	$0x833,	%%rcx	\n\t"    //Thermal Monitor
            "wrmsr	\n\t"
            "movq 	$0x834,	%%rcx	\n\t"    //Performance Counter
            "wrmsr	\n\t"
            "movq 	$0x835,	%%rcx	\n\t"    //LINT0
            "wrmsr	\n\t"
            "movq 	$0x836,	%%rcx	\n\t"    //LINT1
            "wrmsr	\n\t"
            "movq 	$0x837,	%%rcx	\n\t"    //Error
            "wrmsr	\n\t"
    :
    :"a"(0x10000), "d"(0x00)
    :"memory");

    printfColor(GREEN, BLACK, "Mask ALL LVT\n");

    // 12. 获取 TPR 寄存器的值
    __asm__ __volatile__(    "movq 	$0x808,	%%rcx	\n\t"
            "rdmsr	\n\t"
    :"=a"(x), "=d"(y)
    :
    :"memory");

    printfColor(GREEN, BLACK, "Set LVT TPR: %lx\n", x);

    // 13. 获取 PPR 寄存器的值
    __asm__ __volatile__(    "movq 	$0x80a,	%%rcx	\n\t"
            "rdmsr	\n\t"
    :"=a"(x), "=d"(y)
    :
    :"memory");

    printfColor(GREEN, BLACK, "Set LVT PPR: %lx\n", x);

}

void IOAPICPageTableRemap() {

    unsigned long *tmpAddress;
    unsigned char *IOAPICAddress = (unsigned char *) Physical_To_Virtual(0xfec00000);

    // 1. 设置 I/O APIC 映射全局变量的值
    ioapicMap.physicalAddress = 0xfec00000;
    ioapicMap.virtualIndexAddress = IOAPICAddress;
    ioapicMap.virtualDataAddress = (unsigned int *) (IOAPICAddress + 0x10);
    ioapicMap.virtualEOIAddress = (unsigned int *) (IOAPICAddress + 0x40);

    // 2. 获取当前 CR3 寄存器的值
    globalCR3 = getCR3();

    // 3. 获取 PML4 的地址
    tmpAddress = Physical_To_Virtual(globalCR3 + (((unsigned long) IOAPICAddress >> PAGE_GDT_SHIFT) & 0x1ff));

    // 4. 判断 PML4 页表项的值，如果为 0 ，就说明没有次级页表，需要设置一下
    if (*tmpAddress == 0) {

        unsigned long *virtual = kmalloc(PAGE_4K_SIZE, 0);
        set_mpl4t(tmpAddress, mk_mpl4t(Virtual_To_Physical(virtual), PAGE_KERNEL_GDT));

    }

    printfColor(YELLOW, BLACK, "1: %lx, %lx\n", (unsigned long) tmpAddress, (unsigned long) *tmpAddress);

    // 5. 获取 PDP 的地址
    tmpAddress = Physical_To_Virtual(
            (unsigned long *) (*tmpAddress & (~0xfffUL)) + (((unsigned long) IOAPICAddress >> PAGE_1G_SHIFT) & 0x1ff));

    // 6. 判断 PDP 页表项的值，如果为 0 ，就说明没有次级页表，需要设置一下
    if (*tmpAddress == 0) {
        unsigned long *virtual = kmalloc(PAGE_4K_SIZE, 0);
        set_pdpt(tmpAddress, mk_pdpt(Virtual_To_Physical(virtual), PAGE_KERNEL_Dir));
    }

    printfColor(YELLOW, BLACK, "2: %lx, %lx\n", (unsigned long) tmpAddress, (unsigned long) *tmpAddress);

    // 7. 获取 PD 的地址
    tmpAddress = Physical_To_Virtual(
            (unsigned long *) (*tmpAddress & (~0xfffUL)) + (((unsigned long) IOAPICAddress >> PAGE_2M_SHIFT) & 0x1ff));

    // 8. 设置 PD 页表
    set_pdt(tmpAddress, mk_pdt(ioapicMap.physicalAddress, PAGE_KERNEL_Page | PAGE_PWT | PAGE_PCD));

    printfColor(BLUE, BLACK, "3: %lx, %lx\n", (unsigned long) tmpAddress, (unsigned long) *tmpAddress);

    printfColor(BLUE, BLACK, "ioapic_map.physical_address: %lx\n", ioapicMap.physicalAddress);
    printfColor(BLUE, BLACK, "ioapic_map.virtual_address: %lx\n",
                (unsigned long) ioapicMap.virtualIndexAddress);

    // 刷新 CR3，以让刚设置的页表生效
    flushTLB();

}

unsigned long ioapicRTERead(unsigned char index) {

    unsigned long ret;

    // 1. 设置要读取的高32位的索引
    *ioapicMap.virtualIndexAddress = (unsigned char) (index + 1);

    // 2. 读取高 32 位
    mfence();
    ret = *ioapicMap.virtualDataAddress;
    ret <<= 32;
    mfence();

    // 3. 设置要读取的低32位索引
    *ioapicMap.virtualIndexAddress = index;

    // 4. 读取低 32 位
    mfence();
    ret |= *ioapicMap.virtualDataAddress;
    mfence();

    return ret;

}

void ioapicRTEWrite(unsigned char index, unsigned long value) {

    // 1. 设置要写入的低32位索引
    *ioapicMap.virtualIndexAddress = index;

    // 2. 写入低32位
    mfence();
    *ioapicMap.virtualDataAddress = (unsigned int) (value & 0xffffffff);
    value >>= 32;
    mfence();

    // 3. 设置要写入的高32位索引
    *ioapicMap.virtualIndexAddress = (unsigned char) (index + 1);

    // 4. 写入高32位
    mfence();
    *ioapicMap.virtualDataAddress = (unsigned int) (value & 0xffffffff);
    mfence();

}

void IOAPICInit() {

    int i;

    // 1. 设置 I/O APIC ID值的索引
    *ioapicMap.virtualIndexAddress = 0x00;

    // 2. 设置 I/O APIC ID 值为 0x0f000000
    mfence();
    *ioapicMap.virtualDataAddress = 0x0f000000;
    mfence();

    printfColor(GREEN, BLACK, "Get IOAPIC ID REG: %lx, ID: %lx\n", *ioapicMap.virtualDataAddress,
                *ioapicMap.virtualDataAddress >> 24 & 0xf);
    mfence();

    // 3. 设置 I/O APIC 版本号的索引
    *ioapicMap.virtualIndexAddress = 0x01;

    mfence();

    printfColor(GREEN, BLACK, "Get IOAPIC Version REG: %lx, MAX redirection enties: %d\n",
                *ioapicMap.virtualDataAddress, ((*ioapicMap.virtualDataAddress >> 16) & 0xff) + 1);

    // 4. 初始化所有 RTE 表项，
    for (i = 0x10; i < 0x40; i += 2)
        ioapicRTEWrite(i, (unsigned long) (0x10020 + ((i - 0x10) >> 1)));

    // 5. 开启 RTE1 表项来着键盘中断请求
    ioapicRTEWrite(0x12, 0x21);

    printfColor(GREEN, BLACK, "I/O APIC Redirection Table Entries Set Finished.\n");

}


void IOAPICEnable(unsigned long irq) {
    unsigned long value = 0;
    value = ioapicRTERead((unsigned char) ((irq - 32) * 2 + 0x10));
    value = value & (~0x10000UL);
    ioapicRTEWrite((unsigned char) ((irq - 32) * 2 + 0x10), value);
}

void IOAPICDisable(unsigned long irq) {
    unsigned long value = 0;
    value = ioapicRTERead((unsigned char) ((irq - 32) * 2 + 0x10));
    value = value | 0x10000UL;
    ioapicRTEWrite((unsigned char) ((irq - 32) * 2 + 0x10), value);
}

unsigned long IOAPICInstall(unsigned long irq, void *arg) {
    IOAPICRETEntry *entry = (IOAPICRETEntry *) arg;
    ioapicRTEWrite((unsigned char) ((irq - 32) * 2 + 0x10), *(unsigned long *) entry);

    return 1;
}

void IOAPICUninstall(unsigned long irq) {
    ioapicRTEWrite((unsigned char) ((irq - 32) * 2 + 0x10), 0x10000UL);
}

void IOAPICLevelAck(unsigned long irq) {
    __asm__ __volatile__(    "movq	$0x00,	%%rdx	\n\t"
            "movq	$0x00,	%%rax	\n\t"
            "movq 	$0x80b,	%%rcx	\n\t"
            "wrmsr	\n\t"
    :: :"memory");

    *ioapicMap.virtualEOIAddress = (unsigned int) irq;
}

void IOAPICEdgeAck(unsigned long irq) {
    __asm__ __volatile__(    "movq	$0x00,	%%rdx	\n\t"
            "movq	$0x00,	%%rax	\n\t"
            "movq 	$0x80b,	%%rcx	\n\t"
            "wrmsr	\n\t"
    :: :"memory");
}

void LocalAPICEdgeLevelAck(unsigned long irq) {
    __asm__ __volatile__(    "movq	$0x00,	%%rdx	\n\t"
            "movq	$0x00,	%%rax	\n\t"
            "movq 	$0x80b,	%%rcx	\n\t"
            "wrmsr	\n\t"
    :: :"memory");
}


void doIRQ(PerformSite *regs, unsigned long nr) {

    unsigned char x;
//    irq_desc_T * irq = &interrupt_desc[nr - 32];

//    x = io_in8(0x60);
//    printfColor(BLUE, WHITE, "(IRQ: %lx) key code: %lx\n", nr, x);
//
////    if(irq->handler != NULL)
////        irq->handler(nr,irq->parameter,regs);
////
////    if(irq->controller != NULL && irq->controller->ack != NULL)
////        irq->controller->ack(nr);
//
//    __asm__ __volatile__(    "movq	$0x00,	%%rdx	\n\t"
//            "movq	$0x00,	%%rax	\n\t"
//            "movq 	$0x80b,	%%rcx	\n\t"
//            "wrmsr	\n\t"
//    :: :"memory");

    switch (nr & 0x80) {
        case 0x00: {
            IRPDescriptorTable *irq = &interruptDescriptor[nr - 32];

            if (irq->handler != NULL)
                irq->handler(nr, irq->parameter, regs);

            if (irq->controller != NULL && irq->controller->ack != NULL)
                irq->controller->ack(nr);
        }
            break;

        case 0x80:


            printfColor(RED, BLACK, "SMP IPI: %d, CPUID: %d\n", nr, SMPCPUID());
            LocalAPICEdgeLevelAck(nr);

            {
                IRPDescriptorTable *irq = &interruptDescriptor[nr - 200];

                if (irq->handler != NULL)
                    irq->handler(nr, irq->parameter, regs);
            }

            break;

        default:

            printfColor(RED, BLACK, "do_IRQ receive: %d\n", nr);
            break;
    }

}